package com.avengers.core.kit;

import com.avengers.core.bean.enums.Charset;
import com.avengers.core.bean.enums.SymbolEnum;
import com.jfinal.kit.StrKit;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.NameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author yu.wang
 * @since 2019-07-15 上午10:14
 */
public class StringKit extends StrKit {

    private static final Logger logger = LoggerFactory.getLogger(StringKit.class);

    /**
     * 隐藏手机
     *
     * @param phone 手机号码
     * @return 隐藏后
     */
    public static String hidePhone(String phone) {
        if (StrKit.isBlank(phone)) {
            return "";
        }
        if (RegexKit.match(RegexKit.MOBILE, phone)) {
            String begin = phone.substring(0, 3);
            String end = phone.substring(7);
            return begin + "****" + end;
        }
        return phone;
    }

    /**
     * 隐藏邮箱
     *
     * @param mail 电子邮箱
     * @return 隐藏后
     */
    public static String hideMail(String mail) {
        if (StrKit.isBlank(mail)) {
            return "";
        }
        if (RegexKit.match(RegexKit.EMAIL, mail)) {
            String prefixMail = mail.substring(0, mail.indexOf("@"));
            BigDecimal lengthBigDecimal = new BigDecimal(prefixMail.length());

            BigDecimal halfCeilLength = lengthBigDecimal.divide(new BigDecimal(2), 0, RoundingMode.HALF_UP);
            BigDecimal halfFloorLength = lengthBigDecimal.divide(new BigDecimal(2), 0, RoundingMode.HALF_DOWN);

            String front = prefixMail.substring(0, halfFloorLength.intValue() / 2);
            String end = prefixMail.substring(halfFloorLength.intValue() / 2 + halfCeilLength.intValue());
            StringBuilder middle = new StringBuilder();
            int hideLength = Math.min(halfCeilLength.intValue(), 4);
            for (int i = 0; i < hideLength; i++) {
                middle.append("*");
            }
            return middle.insert(0, front).append(end).append(mail.substring(mail.indexOf("@"))).toString();
        }
        return mail;
    }

    /**
     * 隐藏身份证
     *
     * @param idCard 身份证
     * @return 隐藏后
     */
    public static String hideIdCard(String idCard) {
        if (StrKit.isBlank(idCard)) {
            return "";
        }
        if (RegexKit.match(RegexKit.ID_CARD, idCard)) {
            String begin = idCard.substring(0, 6);
            String end = idCard.substring(idCard.length() - 1);
            return begin + "****" + end;
        }
        return idCard;
    }

    @SafeVarargs
    public static <T> String concat(SymbolEnum s, T... objects) {
        return concat(s, null, objects);
    }

    @SafeVarargs
    public static <T> String concat(String s, T... objects) {
        return concat(s, null, objects);
    }

    @SafeVarargs
    public static <T> String concat(SymbolEnum s, Integer itemMax, T... objects) {
        return concat(s.string, itemMax, objects);
    }

    @SafeVarargs
    public static <T> String concat(String s, Integer itemMax, T... objects) {
        return concat(s, itemMax, false, objects);
    }

    @SafeVarargs
    public static <T> String concat(String s, Integer itemMax, boolean showLength, T... objects) {
        if (null == objects) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < objects.length; i++) {
            if (null == objects[i] || objects[i].toString().isEmpty()) {
                continue;
            }
            if (0 != i) {
                sb.append(s);
            }
            if (null == itemMax || 0 == itemMax) {
                sb.append(objects[i]);
            } else {
                String value = objects[i].toString();
                if (value.length() > itemMax) {
                    sb.append(value, 0, itemMax);
                    if (showLength) {
                        sb.append("(L").append(value.length()).append(")");
                    }
                } else {
                    sb.append(value);
                }
            }
        }
        return sb.toString();
    }

    public static String concat(SymbolEnum s, Iterable<?> iterable) {
        return concat(s, null, iterable);
    }

    public static String concat(String s, Iterable<?> iterable) {
        return concat(s, null, iterable);
    }

    public static String concat(SymbolEnum s, Integer itemMax, Iterable<?> iterable) {
        return concat(s.string, itemMax, iterable);
    }

    public static String concat(String s, Integer itemMax, Iterable<?> iterable) {
        return concat(s, itemMax, false, iterable);
    }

    public static String concat(String s, Integer itemMax, boolean showLength, Iterable<?> iterable) {
        if (null == iterable) {
            return "";
        }
        Iterator<?> iterator = iterable.iterator();
        StringBuilder sb = new StringBuilder();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            if (null != o && !o.toString().isEmpty()) {
                if (sb.length() > 0) {
                    sb.append(s);
                }
                if (null == itemMax || 0 == itemMax) {
                    sb.append(o);
                } else {
                    String value = o.toString();
                    if (value.length() > itemMax) {
                        sb.append(value, 0, itemMax);
                        if (showLength) {
                            sb.append("(L").append(value.length()).append(")");
                        }
                    } else {
                        sb.append(value);
                    }
                }
            }
        }
        return sb.toString();
    }

    /**
     * 生成的随机数类型
     */
    public enum RandomType {
        /**
         *
         */
        INT, STRING, ALL
    }

    public static String random(int count) {
        return random(count, RandomType.ALL);
    }

    /**
     * 随机数生成
     *
     * @param count      长度
     * @param randomType 字符串类型
     * @return 随机字符串
     */
    public static String random(int count, RandomType randomType) {
        if (count == 0) {
            return "";
        }
        if (count < 0) {
            throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
        }
        switch (randomType) {
            case INT:
                return RandomStringUtils.randomNumeric(count);
            case STRING:
                return RandomStringUtils.randomAlphabetic(count);
            case ALL:
            default:
                return RandomStringUtils.randomAlphanumeric(count);
        }
    }

    public static String mapToHttpParams(Map<String, String> paramMap, String... excludeNames) {
        return mapToHttpParams(paramMap, false, excludeNames);
    }

    public static String mapToHttpParams(Map<String, String> paramMap, boolean encodeValue) {
        return mapToHttpParams(paramMap, encodeValue ? Charset.UTF8 : null);
    }

    public static String mapToHttpParams(Map<String, String> paramMap, boolean encodeValue, String... excludeNames) {
        return mapToHttpParams(paramMap, encodeValue ? Charset.UTF8 : null, excludeNames);
    }

    public static String mapToHttpParams(Map<String, String> paramMap, Charset encodeCharset, String... excludeNames) {
        if (null != paramMap && !paramMap.keySet().isEmpty()) {
            List<String> excludeNameList = null == excludeNames || excludeNames.length < 1 ? Collections.emptyList() : Arrays.asList(excludeNames);
            List<String> keyList = new ArrayList<>(paramMap.keySet());
            Collections.sort(keyList);
            StringBuilder paramSB = new StringBuilder();

            for (String name : keyList) {
                if (excludeNameList.contains(name)) {
                    continue;
                }
                if (!isBlank(paramMap.get(name))) {
                    paramSB.append("&").append(name).append("=").append(null != encodeCharset ? EncryptKit.urlEncode(paramMap.get(name), encodeCharset) : paramMap.get(name));
                }
            }

            return paramSB.length() > 1 ? paramSB.substring(1) : paramSB.toString();
        } else {
            return "";
        }
    }

    public static String mapToHttpArrayParams(Map<String, String[]> httpParam, String... excludeNames) {
        return mapToHttpArrayParams(httpParam, false, excludeNames);
    }

    public static String mapToHttpArrayParams(Map<String, String[]> httpParam, boolean encodeValue) {
        return mapToHttpArrayParams(httpParam, encodeValue ? Charset.UTF8 : null);
    }

    public static String mapToHttpArrayParams(Map<String, String[]> httpParam, boolean encodeValue, String... excludeNames) {
        return mapToHttpArrayParams(httpParam, encodeValue ? Charset.UTF8 : null, excludeNames);
    }

    public static String mapToHttpArrayParams(Map<String, String[]> httpParam, Charset encodeCharset, String... excludeNames) {
        if (null == httpParam || httpParam.keySet().isEmpty()) {
            return "";
        }
        List<String> excludeNameList = null == excludeNames || excludeNames.length < 1 ? Collections.emptyList() : Arrays.asList(excludeNames);
        List<String> keyList = new ArrayList<>(httpParam.keySet());
        Collections.sort(keyList);

        StringBuilder paramSB = new StringBuilder();
        for (String name : keyList) {
            if (excludeNameList.contains(name)) {
                continue;
            }
            String[] values = httpParam.get(name);

            if (null == values || values.length < 1) {
                continue;
            }
            for (String value : values) {
                paramSB.append("&").append(name).append("=").append(null != encodeCharset ? EncryptKit.urlEncode(value, encodeCharset) : value);
            }
        }

        return paramSB.length() > 1 ? paramSB.substring(1) : paramSB.toString();
    }

    /**
     * 将参数列表格式化为供签名使用的格式
     *
     * @param params 参数
     * @return 序列化参数
     */
    public static String formatStrForSign(List<NameValuePair> params) {
        return formatStrForSign(params, false);
    }

    public static String formatStrForSign(List<NameValuePair> params, boolean urlEncodeValue) {
        StringBuilder formatStr = new StringBuilder();
        for (NameValuePair nvp : params) {
            if (urlEncodeValue) {
                formatStr.append("&").append(nvp.getName()).append(SymbolEnum.equalSign.string).append(EncryptKit.urlEncode(nvp.getValue()));
            } else {
                formatStr.append("&").append(nvp);
            }
        }
        return formatStr.length() > 1 ? formatStr.substring(1) : formatStr.toString();
    }

    /**
     * @param string 字符串
     * @return MD5
     */
    public static String md5(String string) {
        try {
            return isBlank(string) ? string : DigestUtils.md5Hex(string.getBytes(Charset.UTF8.value));
        } catch (UnsupportedEncodingException e) {
            return string;
        }
    }

    public static String genOrderCode() {
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssSSS");
        return dateFormat.format(now) + RandomStringUtils.randomNumeric(3);
    }

    public static boolean isChinese(String strName) {
        char[] ch = strName.toCharArray();

        for (char c : ch) {
            if (isChinese(c)) {
                return true;
            }
        }

        return false;
    }

    private static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
    }

    public static boolean hasChinese(String name) {
        return RegexKit.hasChinese(name);
    }

    /**
     * @deprecated
     */
    public static String getRandomNumStr(int intLength) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < intLength; ++i) {
            sb.append(random.nextInt(10));
        }

        return sb.toString();
    }

    /**
     * 城市
     */
    private static final HashMap<Integer, String> CITY_MAP = new HashMap<Integer, String>() {{
        put(11, "北京");
        put(12, "天津");
        put(13, "河北");
        put(14, "山西");
        put(15, "内蒙古");
        put(21, "辽宁");
        put(22, "吉林");
        put(23, "黑龙江");
        put(31, "上海");
        put(32, "江苏");
        put(33, "浙江");
        put(34, "安徽");
        put(35, "福建");
        put(36, "江西");
        put(37, "山东");
        put(41, "河南");
        put(42, "湖北");
        put(43, "湖南");
        put(44, "广东");
        put(45, "广西");
        put(46, "海南");
        put(50, "重庆");
        put(51, "四川");
        put(52, "贵州");
        put(53, "云南");
        put(54, "西藏");
        put(61, "陕西");
        put(62, "甘肃");
        put(63, "青海");
        put(64, "宁夏");
        put(65, "新疆");
        put(71, "台湾");
        put(81, "香港");
        put(82, "澳门");
        put(91, "国外");
    }};

    /**
     * 身份证校验
     *
     * @param num 身份证号码
     * @return true验证通过，否则不符合身份证规则
     */
    public static boolean validateIdCard(String num) {
        int iSum = 0;
        if (!num.matches(RegexKit.ID_CARD)) {
            return false;
        }

        num = num.replaceAll("[xX]$", "a");
        int subLength = 2;
        if (StrKit.isBlank(CITY_MAP.get(Integer.parseInt(num.substring(0, subLength))))) {
            return false;
        }
        String sBirthday = num.substring(6, 10) + "-" + num.substring(10, 12) + "-" + num.substring(12, 14);
        SimpleDateFormat sdf = new SimpleDateFormat(RegexKit.DATE_FORMAT);
        Date d;
        try {
            d = sdf.parse(sBirthday);
        } catch (ParseException e) {
            logger.error(e.getMessage(), e);
            return false;
        }
        Calendar cld = Calendar.getInstance();
        cld.setTime(d);
        String mouth = String.valueOf((cld.get(Calendar.MONTH) + 1));
        if (mouth.length() == 1 && !mouth.contains(0 + "")) {
            mouth = "0" + mouth;
        }

        String day = String.valueOf(cld.get(Calendar.DAY_OF_MONTH));
        if (day.length() == 1 && !day.contains(0 + "")) {
            day = "0" + day;
        }

        if (!sBirthday.equals((cld.get(Calendar.YEAR)) + SymbolEnum.minus.toString() + mouth + SymbolEnum.minus.toString() + day)) {
            return false;
        }

        int maxIndex = 17;
        for (int i = maxIndex; i >= 0; i--) {
            iSum += (int) ((Math.pow(2, i) % 11) * Integer.valueOf(String.valueOf(num.charAt(17 - i)), 11));
        }
        int remValue = 11;
        return iSum % remValue == 1;
    }

    /**
     * 首字母变小写
     */
    public static String firstCharToLowerCase(String str) {
        if (isBlank(str)) {
            return SymbolEnum.empty.string;
        }
        return StrKit.firstCharToLowerCase(str);
    }

    /**
     * 首字母变大写
     */
    public static String firstCharToUpperCase(String str) {
        if (isBlank(str)) {
            return SymbolEnum.empty.string;
        }
        return StrKit.firstCharToUpperCase(str);
    }
}
